package IC.Types;

import IC.AST.ICClass;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: nimrod
 * Date: 09/01/12
 * Time: 15:51
 * To change this template use File | Settings | File Templates.
 */
public class TypeTable {
    private Map<String, MethodType> methodTypes = new LinkedHashMap<String, MethodType>();
    private Map<String, ClassType> classTypes = new LinkedHashMap<String, ClassType>();
    private Map<Type, ArrayType> arrayTypes = new LinkedHashMap<Type, ArrayType>();

    private PrimitiveType intType = new PrimitiveType(PrimitiveTypeKind.INT);
    private PrimitiveType boolType = new PrimitiveType(PrimitiveTypeKind.BOOLEAN);
    private PrimitiveType nullType = new PrimitiveType(PrimitiveTypeKind.NULL);
    private PrimitiveType stringType = new PrimitiveType(PrimitiveTypeKind.STRING);
    private PrimitiveType voidType = new PrimitiveType(PrimitiveTypeKind.VOID);

    private String fileName;

    public TypeTable(String fileName) {
        this.fileName = fileName;
    }

    public MethodType getMethodType(Type[] parameterTypes, Type returnType) {
        String methodTypeName =  MethodType.getMethodTypeName(parameterTypes, returnType);
        if (!methodTypes.containsKey(methodTypeName)) {
            MethodType methodType = new MethodType(parameterTypes, returnType, methodTypeName);
            methodTypes.put(methodTypeName, methodType);
            return methodType;
        }

        return methodTypes.get(methodTypeName);
    }

    public ClassType getClassTypeByName(String className) {
        if (!classTypes.containsKey(className)) {
            return null;
        }

        return classTypes.get(className);
    }

    public ClassType getClassType(ICClass icClass) {
        if (!classTypes.containsKey(icClass.getName())) {
            ClassType superType = null;
            if (icClass.hasSuperClass()) {
                superType = getClassTypeByName(icClass.getSuperClassName());

                //trying to create new class, when the superclass wasn't created yet - error.
                if (superType == null) {
                    return null;
                }

                icClass.setSuperContextType(superType);
            }

            ClassType classType = new ClassType(icClass, superType);
            classTypes.put(icClass.getName(), classType);

            icClass.setContextType(classType);
            return classType;
        } else {
            ClassType ct = classTypes.get(icClass.getName());
            if (ct.getIcClass() != icClass) {
                // in this case two classes with the same already exists
                return null;
            }
            return ct;
        }
    }

    public ArrayType getArrayType(Type elementType) {
        if (!arrayTypes.containsKey(elementType)) {
            ArrayType arrType = new ArrayType(elementType);
            arrayTypes.put(elementType, arrType);
            return arrType;
        }

        return arrayTypes.get(elementType);
    }

    public Type getArrayType(Type elementType, int dimension) {

        Type currentArrayType = elementType;

        for (int i = 0; i < dimension; i++) {
            currentArrayType = getArrayType(currentArrayType);
        }

        return currentArrayType;
    }

    public PrimitiveType getBoolType() {
        return boolType;
    }

    public PrimitiveType getIntType() {
        return intType;
    }

    public PrimitiveType getStringType() {
        return stringType;
    }

    public PrimitiveType getNullType() {
        return nullType;
    }

    public PrimitiveType getVoidType() {
        return voidType;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("Type Table: ");
        sb.append(fileName).append('\n');

        sb.append("    ");
        sb.append(intType.toString()).append('\n');
        sb.append("    ");
        sb.append(boolType.toString()).append('\n');
        sb.append("    ");
        sb.append(nullType.toString()).append('\n');
        sb.append("    ");
        sb.append(stringType.toString()).append('\n');
        sb.append("    ");
        sb.append(voidType.toString()).append('\n');

        for(ClassType ct : this.classTypes.values()) {
            sb.append("    ");
            sb.append(ct.toString()).append('\n');
        }

        for(ArrayType at : this.arrayTypes.values()) {
            sb.append("    ");
            sb.append(at.toString()).append('\n');
        }

        for(MethodType mt : this.methodTypes.values()) {
            sb.append("    ");
            sb.append(mt.toString()).append('\n');
        }

        return sb.toString();
    }
}